high12noon blog

Why C?

Back to main page

Page created March 24, 2024; last updated (to fix typos) April 8th, 2024.

Why C? Why not Pascal, Rust, Python, Java, JavaScript, or another language?

C is not the perfect programming language. Although the language itself is relatively simple in terms of syntax, C can be hard because C makes it notoriously easy to make dumb mistakes that result in dangerous bugs and potential for security exploits.

It is easy to dog on C for its shortcomings, but before we do that, let's first look at its most important strength. And, since this article is about why I use C, I'll touch upon how C's biggest strength relates to my work. Perhaps some of this article will be applicable to you as well.

The most important strength of C, at least in my line of work, is its future life expectancy.

This language was created in the 1970s and published in book form in 1978. In the subsequent decade, it became adopted so widely that the American National Standards Institute (ANSI) standardized it in 1989 ("C89") and the International Organization for Standardization (ISO) standardized it in 1990 ("C90"). (ANSI C89 and ISO C90 are basically the same thing, just American vs International, with some formatting changes.)

If we consider that standard today, in 2024, it means that an internationally recognized and standardized version of C exists for 35 years. If we consider the first published book form of the language, that brings us to 46 years.

In computer and technology terms, both of these numbers represent a heck of a long time, during which time computers have shrunk from filling entire rooms to fitting in a wristwatch, all while becoming many orders of magnitude more powerful. In the last 46 years, how many other technologies, including programming languages, have risen to prominence only to disappear?

In contrast, consider how well-entrenched C is today:

C has a very large ecosystem of support tooling, including compilers, debuggers, IDEs, testing frameworks, static analyzers, dynamic analyzers, source code formatters, libraries, and much more. Not only are such tools widely available, but there are actually multiple well-established choices for each type of tool available from different developers, both free and proprietary. Even if you only pick from free software of open source origins, you have plenty of options. Consider the GCC and Clang toolchains, to name just two examples.

A great deal of the world's mission-critical software is implemented in C. This includes all major operating system kernels, which means that no matter what language is used to write a program, that program cannot run without the help of another program (the OS kernel), which is written in C. In addition, the interpreters for most interpreted languages are written in C. For example, Python is a hugely popular language, but programs written in Python run in the Python interpreter, which is written in C. Software requiring high performance is written in C. For example, OpenCV (computer vision and AI) is written in C.

This kind of entrenchment is not easily undone. Even if the world decided to abandon C and rewrite all important software in another language—assuming that "everyone" could even agree on what other language to use, and assuming that the replacement programs would actually gain enough adoption to displace and replace the C-coded originals—even if that happened, it would take a stupendously long time, during which time C would continue to be available, in widespread use, and probably better supported than the replacement language.

This can all be summed up in a principle called the Lindy Effect, which says that the future life of non-perishable things is expected to be proportional to their current age. We don't know the future, but given C's track record and its being so solidly established, my best guess is that C will be around and supported for quite a while yet.

Why do we care about the future life expectancy of a programming language? The most important reason is the life cycle of our products. There is a big difference between, say, a video game app, which might have a life cycle measured in months, and the kind of automation equipment and technical infrastructure that puts bread and butter on my table, which has a life cycle measured in decades. Over the course of the product life cycle, the hardware is likely to become obsolete and be replaced by better hardware; the software, on the other hand, must continue to be supported, developed, and improved, even as it migrates across hardware iterations. For this, you need a language that is likely to be around and still well-supported.

We know about C's limitations, the biggest of which is its "unsafety." There's plenty of room to argue in favor of a "safer" programming language. But which language would you pick?

One could argue (in fact, one has argued with me, which is why I'm addressing this here) that, say, Pascal never had nor needed something like a linter because the language is "safe." I'll argue that this is not true. The person who argued that point was my mentor and is a big fan of Borland's rendition of Pascal, which was hugely popular in the 1980s and 1990s. I had the pleasure of trying it when I was first learning to code and I can testify that it was indeed a fabulous product that made Pascal useful for real work. I think (but don't know 100% for certain) that the Pascal language as originally designed by Niklaus Wirth lacked things like pointers, and if it didn't lack them, then it certainly lacked pointer arithmetic. Borland's rendition had support for pointers, pointer arithmetic, and probably other things that weren't part of the original language, for the very pragmatic reason that these are useful and necessary for certain kinds of Real Work. Pointers can be abused and misused in Borland Pascal just as much as in C, and with the same consequences. Furthermore, even though Pascal performs automatic bounds checking on array accesses (something that is available in C nowadays, I recall reading recently, but cannot seem to find the link at the moment), if your Pascal program attempts a runtime access to an out-of-bounds array element, your program crashes immediately with a runtime error. (This is the same as a failed assertion in C.) In other words, bad things do happen when you do bad things in Pascal. The lack of a linter (if it's true that there isn't one) is, in my view, a disadvantage rather than a testament to Pascal's safety, because it deprives us of a tool that could help catch such errors. But this argument is a moot point because much to our chagrin, Pascal has largely fallen out of favor, even though toolchains like FreePascal and GNU Pascal exist. It has become a niche language, and as such, it is too risky for a project requiring longevity, a large ecosystem, and widespread support. Sadly, I think it's in the past.

So let's try to foretell the future: What about Rust? Nowadays, Rust seems to be all the rage around the Internet. There is even hype that both Microsoft and the Linux kernel developers would begin to use Rust in their OS kernels. That's good for Rust, right? But if you dig deeper, at least right now in mid 2024, you'll find that it seems to be more hype than real action. The Linux kernel developers themselves have not yet established how Rust would be used there. It seems they're only dipping their toes into it carefully by only implementing some select small parts in Rust, and it seems to me, as someone on the outside watching to see what happens, that there are some important concerns yet to be addressed. Such as what, you ask? Well, remember when we talked about C being standardized by ANSI and ISO? There is no such standard for Rust. Not only that, but the language is still evolving, even as people write software with it. That means that Rust programs that are perfectly valid today may malfunction or stop working entirely tomorrow, because a new and improved iteration of the language makes something work differently than it used to. That's probably not a big deal if you're writing a video game, to use my earlier example. By then, you'll probably write your next game. But it's a huge deal if you're trying to write long-lived infrastructure!

Is "everyone" going to adopt Rust universally enough to answer our concerns about longevity, standardization, ecosystem, support, etc? The proponents of Rust certainly would like that to happen and they make grandiose claims about it being "The world's most loved language"—but I have yet to see anything that convinces me that Rust is indeed that future C replacement.

What I do see is fragmentation and duplication of effort. The world is in search of a programming language that is better and safer than C, but the world cannot yet agree on what language that is. While there is hype about Rust (and it may turn out to be that one language, who knows?), there are too many other people who are trying to invent that sought-after new programming language that will solve all of our problems.

Indeed, it seems like every time I turn around, I hear about yet another new programming language. At some point, I started to write down their names in a list. That list, as it stands today, is (in alphabetical order): Austral, Beef, Berry, Boa, Buzz, C3, Carbon, Cassette, Catala, Chapel, Claro, Crystal, CUE, D (a.k.a. Dlang), Dart, Elixir, F#, Fennel, Flix, Flyde, Go (two different languages with this name?), Hare, Hylo, Idris, Imba, Inko, Jai, Janet, jq, Julia, Kit, Kotlin, MiniLang, Mojo, Nim, Numbat, Odin, Onyx, Perun2, Pony, Pure, Pyro, Reason, Red, Roc, Rune, Rust, Sage, scrapscript.py, Shen, Tal, Templ, Unison, V, Val, Vala, Vale, Virgil, Zig, and Zimbu.

In the past, whenever I heard about a new programming language, I used to spend some time to look into it. What's so cool, or special, or useful about this new language? But I don't bother anymore because there are just so many of the darn things, and when you really boil them down to the essence, you find that most of them are regurgitations of things that were already done in other languages, sometimes going back years if not decades, but perhaps with some syntactic eye candy that looks new and groovy. So, I don't know what most of the languages in the above list are, or are for. I'm sure that not all of them are "general-purpose" programming languages. Surely some of them are domain-specific languages (DSLs).

Nevertheless, of the above list, the most popular appear to me to be Rust, Zig, and Nim, in that order. All three appear to be still under development, and none of the three have been standardized.

The fragmentation and duplication of effort that I already touched upon is what bothers me most about the proliferation of so many programming languages, new and old. Different things are being implemented in a cacophony of different languages, and therefore many of those things are bound to die out together with the languages in which they are written.

I think it would be a much better and smarter thing if the world would just get behind the language that is already the most entrenched, namely C, and concentrate on improving that. Make better and smarter compilers, linters, testers and analyzers of all kinds, libraries, and development tools for C. By improving the language that already powers so much of the world's technology, and by improving our skills with it, I think we'll get to a safer programming landscape far more efficiently than by fragmenting our efforts more than we already have by trying to reinvent the wheel again and again.


Back to main page

Send feedback to: the name of this blog at mail dot com.